package com.dss.sdk.api.base;

import com.dss.sdk.base.BaseClient;
import com.dss.sdk.config.HttpConfig;
import com.dss.sdk.constants.Constants;
import com.dss.sdk.enums.SignDataEnum;
import com.dss.sdk.enums.SignTypeEnum;
import com.dss.sdk.exception.ApiException;
import com.dss.sdk.exception.ApiRuleException;
import com.dss.sdk.file.FileItem;
import com.dss.sdk.request.DssRequest;
import com.dss.sdk.request.DssUploadRequest;
import com.dss.sdk.response.DssEntityResponse;
import com.dss.sdk.response.DssResponse;
import com.dss.sdk.stratey.DefaultJsonStrategy;
import com.dss.sdk.stratey.JsonStrategy;
import com.dss.sdk.utils.crypt.DSSCryptUtil;
import com.dss.sdk.utils.http.HttpUtil;
import com.dss.sdk.utils.http.RequestParametersHolder;
import com.dss.sdk.utils.json.ParameterizedTypeBaseRes;
import com.dss.sdk.utils.string.StrUtil;
import com.dss.sdk.utils.url.DssUrlUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 基于REST的客户端
 *
 * @author Fadada
 * @version 2.0.0
 * @date: 2024/7/4
 */
public class DefaultDssClient extends BaseClient implements FddClient {
    private static final Logger log = LoggerFactory.getLogger(DefaultDssClient.class);
    /**
     * 请求参数
     */
    protected String serverUrl;
    protected String signType = SignTypeEnum.SHA256.getValue();
    protected String language = "zh-CN";
    /**
     * 默认连接超时时间为15秒
     */
    protected int connectTimeout = 15000;
    /**
     * 默认响应超时时间为30秒
     */
    protected int readTimeout = 30000;
    /**
     * 是否在客户端校验请求
     */
    protected boolean needCheckRequest = true;
    /**
     * 是否启用响应GZIP压缩
     */
    protected boolean useGzipEncoding = false;
    /**
     * http配置
     */
    private HttpConfig httpConfig;
    /**
     * json串策率
     */
    private JsonStrategy jsonStrategy = new DefaultJsonStrategy();

    /**
     * DefaultDssClient
     *
     * @throws
     * @Param: serverUrl 接口地址
     * @Param: appId 应用id
     * @Param: appSecret 应用Secret
     */
    public DefaultDssClient(String serverUrl, String appId, String appSecret) {
        super(appId, appSecret);
        this.serverUrl = serverUrl;
        setDefaultHttpConfig();
    }

    public DefaultDssClient(String serverUrl, String appId, String appSecret, HttpConfig httpConfig) {
        super(appId, appSecret);
        this.serverUrl = serverUrl;
        if (httpConfig == null) {
            setDefaultHttpConfig();
        } else {
            setHttpConfig(httpConfig);
        }
    }

    public DefaultDssClient(String serverUrl, String appId, String appSecret, int connectTimeout, int readTimeout) {
        super(appId, appSecret);
        this.serverUrl = serverUrl;
        this.connectTimeout = connectTimeout;
        this.readTimeout = readTimeout;
        setDefaultHttpConfig();
    }

    public DefaultDssClient(String serverUrl, String appId, String appSecret, int connectTimeout, int readTimeout, String signType) {
        super(appId, appSecret);
        this.serverUrl = serverUrl;
        this.connectTimeout = connectTimeout;
        this.readTimeout = readTimeout;
        this.signType = signType;
        setDefaultHttpConfig();
    }

    @Override
    public <T> DssResponse<T> execute(DssRequest request, String apiPath, Class<T> clzz) throws ApiException {
        DssResponse response = doExecute(request, apiPath, clzz, false, null);
        if (response == null) {
            return null;
        }
        if (response.getBody() == null) {
            return response;
        }
        DssResponse<T> tRsp;
        try {
            tRsp = getJsonStrategy().toBean(response.getBody(), new ParameterizedTypeBaseRes(clzz));
            tRsp.setBody(response.getBody());
            tRsp.setParams(response.getParams());
            tRsp.setHttpStatusCode(response.getHttpStatusCode());
        } catch (Exception e) {
            log.error("converter toResponse fail appId:{},methodName:{},serverUrl:{},allParams:{},exception:{}", this.getAppId(), apiPath,
                    serverUrl, response.getParams(), e.toString());
            throw new ApiException(e);
        }
        if (!tRsp.isSuccess()) {
            log.error("appId:{},methodName:{},serverUrl:{},allParams:{},exception:{}", this.getAppId(), apiPath,
                    serverUrl, response.getParams(), tRsp.getBody());
        }
        return tRsp;
    }

    @Override
    public <T> DssResponse<List<T>> executeList(DssRequest request, String apiPath, Class<T> clzz) throws ApiException {
        DssResponse response = doExecute(request, apiPath, clzz, false, null);
        if (response == null) {
            return null;
        }
        DssResponse tRsp = getJsonStrategy().toBean(response.getBody(), DssResponse.class);
        tRsp.setBody(response.getBody());
        tRsp.setParams(response.getParams());
        tRsp.setHttpStatusCode(response.getHttpStatusCode());
        Object data = response.getData();
        if (data != null) {
            String jsonStr;
            if (data instanceof String) {
                jsonStr = String.valueOf(data);
            } else {
                jsonStr = getJsonStrategy().toJson(data);
            }
            List<T> lists = getJsonStrategy().toList(jsonStr, clzz);
            tRsp.setData(lists);
        }
        if (!tRsp.isSuccess()) {
            log.error("appId:{},methodName:{},serverUrl:{},allParams:{},exception:{}", this.getAppId(), apiPath,
                    serverUrl, tRsp.getParams(), tRsp.getBody());
        }
        return tRsp;
    }

    /**
     * 下载返回对应实体类
     *
     * @param apiPath 请求路径
     * @return DssResponse
     * @throws ApiException API异常
     */
    @Override
    public DssResponse<DssEntityResponse> executeDownload(DssRequest request, String apiPath) throws ApiException {
        return doExecute(request, apiPath, null, true, null);
    }

    private DssResponse doExecute(DssRequest request, String apiPath, Class clzz, boolean isDown, String session) throws ApiException {
        long start = System.currentTimeMillis();
        // 本地校验请求参数
        if (this.needCheckRequest) {
            try {
                request.validate();
            } catch (ApiRuleException e) {
                DssResponse localResponse = new DssResponse();
                try {
                    localResponse.setData(clzz.newInstance());
                } catch (Exception xe) {
                    throw new ApiException(xe);
                }
                localResponse.setCode(e.getErrCode());
                localResponse.setMessage(e.getErrMsg());
                return localResponse;
            }
        }
        RequestParametersHolder requestHolder = new RequestParametersHolder();
        HashMap appParams = new HashMap(request.toMap());
        requestHolder.setApplicationParams(appParams);
        if (StrUtil.isNotBlank(session)) {
            // 增加参数
            HashMap protocalOptParams = new HashMap(8);
            protocalOptParams.put(Constants.SESSION, session);
            requestHolder.setProtocalOptParams(protocalOptParams);
        }
        // 报文参数
        HashMap headParams = DSSCryptUtil.getHeaderMap(this.getAppId(), signType, language);
        // 添加签名参数
        HashMap protocalMustParams = new HashMap(headParams);
        protocalMustParams.remove(SignDataEnum.LANGUAGE.getName());
        if (request instanceof DssUploadRequest) {
            DSSCryptUtil.getFormSign(protocalMustParams, appParams, this.getAppSecret());
        } else {
            DSSCryptUtil.getSign(protocalMustParams, getJsonStrategy().toJson(appParams), this.getAppSecret());
        }
        headParams.put(SignDataEnum.SIGN.getName(), protocalMustParams.get(SignDataEnum.SIGN.getName()));
        requestHolder.setProtocalMustParams(protocalMustParams);
        // 是否需要压缩响应
        if (this.useGzipEncoding) {
            headParams.put(Constants.ACCEPT_ENCODING, Constants.CONTENT_ENCODING_GZIP);
        }
        // 请求跟踪
        headParams.put(Constants.X_REQUEST_ID, StrUtil.blankToDefault(request.getHttpId(), StrUtil.fastSimpleUUID()));
        try {
            DssResponse res;
            String realServerUrl = DssUrlUtils.getServerUrl(this.serverUrl, apiPath, session, appParams);
            String fullUrl = DssUrlUtils.buildRequestUrl(realServerUrl);
            requestHolder.setRequestUrl(fullUrl);
            if (Constants.METHOD_POST.equals(request.getHttpMethod())) {
                Map<String, FileItem> fileParams = null;
                Object body;
                // 是否需要上传文件
                if (request instanceof DssUploadRequest) {
                    DssUploadRequest uRequest = (DssUploadRequest) request;
                    fileParams = StrUtil.cleanupMap(uRequest.getFileParams());
                    body = appParams;
                } else {
                    body = getJsonStrategy().toJson(appParams);
                }
                if (isDown) {
                    DssEntityResponse baseResponseEntity = requestDown(fullUrl, headParams, (String) body);
                    if (baseResponseEntity.getData() != null) {
                        res = getJsonStrategy().toBean(baseResponseEntity.getData(), DssResponse.class);
                    } else {
                        res = new DssResponse(Constants.STRING_0_FLAG, Boolean.TRUE, apiPath);
                        res.setData(baseResponseEntity);
                    }
                    res.setHttpStatusCode(baseResponseEntity.getHttpStatusCode());
                } else {
                    res = HttpUtil.post(fullUrl, headParams, body, fileParams);
                }
            } else {
                res = HttpUtil.get(fullUrl, headParams, appParams);
            }
            if (res == null) {
                return null;
            }
            res.setParams(requestHolder.getAllParams());
            requestHolder.setResponseBody(res.getBody());
            requestHolder.setHttpStatusCode(res.getHttpStatusCode());
            return res;
        } catch (Exception e) {
            log.error("appId:{},methodName:{},serverUrl:{},allParams:{},resTime:{},exception:{}", this.getAppId(), apiPath,
                    serverUrl, requestHolder.getAllParams(), System.currentTimeMillis() - start, e.toString());
            throw new ApiException(e);
        }
    }

    private DssEntityResponse requestDown(String url, Map<String, String> headerMap, String body) throws ApiException {
        try {
            return HttpUtil.downLoadFiles(url, headerMap, body);
        } catch (Exception e) {
            log.error("httpDownLoad请求失败：{}", e.getMessage(), e);
            throw new ApiException("httpDownLoad请求失败");
        }
    }

    public String getServerUrl() {
        return serverUrl;
    }

    public void setServerUrl(String serverUrl) {
        if (serverUrl != null) {
            this.serverUrl = serverUrl;
        }
    }

    public String getSignType() {
        return signType;
    }

    public void setSignType(String signType) {
        if (signType != null) {
            this.signType = signType;
        }
    }

    public String getLanguage() {
        return language;
    }

    public void setLanguage(String language) {
        if (language != null) {
            this.language = language;
        }
    }

    public int getConnectTimeout() {
        return connectTimeout;
    }

    public void setConnectTimeout(int connectTimeout) {
        this.connectTimeout = connectTimeout;
    }

    public int getReadTimeout() {
        return readTimeout;
    }

    public void setReadTimeout(int readTimeout) {
        this.readTimeout = readTimeout;
    }

    public HttpConfig getHttpConfig() {
        return httpConfig;
    }

    public void setHttpConfig(HttpConfig httpConfig) {
        if (httpConfig != null) {
            this.httpConfig = httpConfig;
            HttpUtil.httpConfig = this.httpConfig;
        }
    }

    public JsonStrategy getJsonStrategy() {
        return jsonStrategy;
    }

    public void setJsonStrategy(JsonStrategy jsonStrategy) {
        if (jsonStrategy != null) {
            this.jsonStrategy = jsonStrategy;
        }
    }

    /**
     * 是否忽略HTTPS证书校验。
     */
    public void setIgnoreSSLCheck(boolean ignore) {
        HttpUtil.setIgnoreSSLCheck(ignore);
    }

    /**
     * 是否启用响应GZIP压缩
     */
    public void setUseGzipEncoding(boolean useGzipEncoding) {
        this.useGzipEncoding = useGzipEncoding;
    }

    /**
     * 是否在客户端校验请求
     */
    public void setNeedCheckRequest(boolean needCheckRequest) {
        this.needCheckRequest = needCheckRequest;
    }

    private void setDefaultHttpConfig() {
        if (httpConfig == null) {
            httpConfig = new HttpConfig(connectTimeout, readTimeout);
        } else {
            httpConfig.setConnectTimeout(connectTimeout);
            httpConfig.setReadTimeout(readTimeout);
        }
        setHttpConfig(httpConfig);
    }
}
